﻿//Copyright (C) <2013>  <jonathan cleeve norton> All Rights Reserved 
//Contact jon.norton@fin-plus.co.uk website <http://www.fin-plus.co.uk/>
using System;
using System.Reflection;
using System.Text.RegularExpressions;
using Excel = Microsoft.Office.Interop.Excel;
using ExcelDna.Integration;
using FinPlusCommon;
using FinPlusDBConn;
using FinPlusWPFControls;
using log = Logger.Logger;
using u = FinPlusUtility.Utility;

namespace FinPlusBooker
{
    public class Booker 
    {
        private static Excel.Application _excel = null;
        private static Excel.Workbook _workbook = null;
        private static Excel.Worksheet _worksheet = null;

        private static IFinPlusDBConn _conn = null;
        private static string _user = Environment.UserName;
        private static u.Env _env = u.Env.unknown;
        private static u.Config _config;
        private static Object _lock = new Object();

        //excel function 
        [ExcelFunction(Description = "load from db and save to db", Category = "FinPlus.Booking")]
        public static void BookerOpen(string worksheetName, string rangeName)
        {
            try
            {
                Connect();

                var func = FunctionLoad(worksheetName, rangeName, _conn);

                if(func != null) WindowOpen(func, _conn);
                
            }
            catch (Exception e)
            {
                log.Log(Logger.Level.Error, e.Message, e);
                throw new BookerException(string.Format("Booker failed to open {0} {1}", worksheetName, rangeName), e);
            }
        }

        //private
        private static void Connect()
        {
            if (_config != null) return;

            lock (_lock)
            {
                if (_config != null) return;

                var p = u.GetConfigPath(Assembly.GetExecutingAssembly());
                _config = u.ConfigXml(p, out _env);
                log.Initialise(p, _env.ToString());
            }

            if (_conn != null) return;

            lock (_lock)
            {
                if (_conn != null) return;
                _conn = new FinPlusConn();
            }
        }

        private static void WindowOpen(IFunction func, IFinPlusDBConn conn)
        {
            try
            {
                FinPlusWindow.OpenEditor(func, conn, "Booker", _env);
            }
            catch (Exception e)
            {
                log.Log(Logger.Level.Error, e.Message, e);
                throw new BookerException("Booker window failed to open ", e);
            }
        }

        private static IFunction FunctionLoad(string worksheetName, string rangeName, IFinPlusDBConn conn)
        {
            try
            {
                var range = Range(worksheetName, rangeName);
                string formula = range.Formula;
                string name = Regex.Match(formula, @"(?<=\=).*(?=\()").Value;
                string parms = Regex.Match(formula, @"(?<=\().*(?=\))").Value;

                if (name == "FinPlusQry") return FunctionQuery(worksheetName, rangeName, conn, name, parms);//return saved function
                return FunctionParse(worksheetName, rangeName, conn, name, parms);//parse new function
            }
            catch (BookerException e)
            {
                log.Log(Logger.Level.Error, e.Message, e);
                return null;
            }
        }

        private static IFunction FunctionQuery(string worksheetName, string rangeName, IFinPlusDBConn conn, string name, string parms)
        {
            var r = RangeValue(worksheetName, rangeName);
            var ps = parms.Split(',');
            int id = -1;
            if (int.TryParse(r, out id) && ps.Length > 1)
            {
                var q = conn.SelectFuncById(ps[1].ToString().Replace("\"", string.Empty), id);
                q.Execute();
                foreach (var f in q.Functions.Values) return f;
            }
            return null;
        }

        private static IFunction FunctionParse(string worksheetName, string rangeName, IFinPlusDBConn conn, string name, string parms)
        {
            var func = new Function(name);
            var nameInfo = typeof(FinPlusDNA.FinPlusDNA).GetMethod(name);
            var index = 0;
           
            foreach (var parm in parms.Split(','))
            {
                var p = ParamNameAndType(nameInfo, index);
                var item = new Item(index + 4, -1, p[0], RangeValue(worksheetName, parm), p[1], DateTime.MinValue, _user);
                func.TryAdd(item);
                index++;
            }
            return func; 
        }

        private static Excel.Range Range(string worksheetName, string rangeName)
        {
            if (_excel == null)
            {
                lock (_lock)
                {
                    if (_excel == null)
                    {
                        _excel = (Excel.Application)System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application");
                        _workbook = _excel.ActiveWorkbook;
                        _worksheet = _workbook.Worksheets[worksheetName];
                    }
                }
            }
          
            return (Excel.Range)_worksheet.Range[rangeName];
        }

        private static string RangeValue(string worksheetName, string rangeName)
        {
            //if not range then value
            try
            {
                return Range(worksheetName, rangeName).Value2.ToString();
            }
            catch
            {
                return rangeName.Replace("\"", string.Empty).TrimStart();
            }
        }

        private static string[] ParamNameAndType(MethodInfo method, int index)
        {
            string[] retVal = {string.Empty,string.Empty};
            if (method != null && method.GetParameters().Length > index)
            {
                var p = method.GetParameters()[index];
                retVal[0] = p.Name;
                retVal[1] = p.ParameterType.Name;
            }
            return retVal;
        }
    }
}
